package com.yingwei.mining.appletversion;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Panel;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.List;

public class GraphPanel extends Panel implements Runnable, MouseListener,
	MouseMotionListener
{
    GraphApplet graph;
    Image backgroundImage;

    int ntrucks;
    Truck trucks[] = new Truck[300];

    int nnodes;
    Node nodes[] = new Node[1000];

    int nedges;
    Edge edges[] = new Edge[2000];

    int nroutes;
    Route routes[] = new Route[100];

    Thread relaxer;
    boolean showLength;
    boolean showIcon;

    int viewMode = 0;

    public GraphPanel(GraphApplet graph)
    {
	this.graph = graph;
	addMouseListener(this);
    }

    void setBackgroundImage(Image img)
    {
	backgroundImage = img;
    }

    int findNode(String lbl)
    {
	for (int i = 0; i < nnodes; i++)
	{
	    if (nodes[i].lbl.equals(lbl))
	    {
		return i;
	    }
	}
	return addNode(lbl);
    }

    int addNode(String lbl)
    {
	Node n = new Node();
	n.x = 10 + 380 * Math.random();
	n.y = 10 + 380 * Math.random();
	n.lbl = lbl;
	nodes[nnodes] = n;
	return nnodes++;
    }

    void addEdge(String from, String to, int len)
    {
	Edge e = new Edge();
	e.from = findNode(from);
	e.to = findNode(to);
	e.len = len;
	edges[nedges++] = e;
    }

    Edge findEdge(int from, int to)
    {
	for (int i = 0; i < nedges; i++)
	{
	    if(edges[i].from == from && edges[i].to == to)
	    {
		return edges[i];
	    }
	    if(edges[i].from == to && edges[i].to == from)
	    {
		Edge e = new Edge();
		e.from = from;
		e.to = to;
		e.len = edges[i].len;
		return e;
	    }
	}
	return null;
    }

    void addRoute(String[] node, double len)
    {
	Route route = new Route(this);
	List<Edge> tmp = new ArrayList<Edge>();

	for (int i = 0; i < node.length - 1; i++)
	{
	    Edge e = findEdge(Integer.parseInt(node[i]), Integer
		    .parseInt(node[i + 1]));
	    tmp.add(e);
	}
	route.edges = (Edge[]) tmp.toArray(new Edge[0]);
	routes[nroutes++] = route;
    }

    public void run()
    {
	Thread me = Thread.currentThread();
	while (relaxer == me)
	{
	    repaint();
	    try
	    {
		Thread.sleep(10);
	    }
	    catch (InterruptedException e)
	    {
		break;
	    }
	}
    }

    Node pick;
    boolean pickfixed;
    Image offscreen;
    Dimension offscreensize;
    Graphics offgraphics;

    final Color fixedColor = Color.red;
    final Color selectColor = Color.pink;
    final Color edgeColor = Color.black;
    final Color nodeColor = new Color(125, 122, 100);
    final Color stressColor = Color.darkGray;
    final Color arcColor1 = Color.black;
    final Color arcColor2 = Color.pink;
    final Color arcColor3 = Color.red;
    final Color excavatorColor = Color.red;
    final Color unloadAreaColor = Color.green;

    public void paintNode(Graphics g, Node n, FontMetrics fm)
    {
	int x = (int) n.x;
	int y = (int) n.y;
	Image img = null;
	if (n == pick)
	{
	    g.setColor(selectColor);
	} else
	{
	    if (n.device instanceof Excavator)
	    {
		g.setColor(excavatorColor);
		img = n.device.getImage();
	    } else if (n.device instanceof UnloadArea)
	    {
		g.setColor(unloadAreaColor);
		img = n.device.getImage();
	    } else
	    {
		g.setColor(nodeColor);
	    }
	}
	int w = fm.stringWidth(n.lbl) + 10;
	int h = fm.getHeight() + 4;
	if( showIcon )
	{
	    if( img != null )
	    {
		g.drawImage(n.device.getImage(),x,y,this);
	    }
	}
	else 
	{
	    g.fillRect(x - w / 2, y - h / 2, w, h);
	    g.setColor(Color.black);
	    g.drawRect(x - w / 2, y - h / 2, w - 1, h - 1);
	}
	g.drawString(n.lbl, x - (w - 10) / 2, (y - (h - 4) / 2)
		+ fm.getAscent());
    }

    public void control()
    {
	FrameClock.currentSteps++;
	for (int i = 0; i < ntrucks; i++)
	{
	    trucks[i].run();
	}

	for (int i = 0; i < nnodes; i++)
	{
	    if (nodes[i].lbl.charAt(0) == 'e')
	    {
		Device d = nodes[i].device;
		Excavator e = (Excavator)d;
		e.run();
	    }
	}
    }
    
    void updateEdgeLen()
    {
	for (int i = 0; i < nedges; i++)
	{
	    Edge e = edges[i];
	    int x1 = (int) nodes[e.from].x;
	    int y1 = (int) nodes[e.from].y;
	    int x2 = (int) nodes[e.to].x;
	    int y2 = (int) nodes[e.to].y;
	    int len = (int) Math.sqrt((x1 - x2) * (x1 - x2)
		    + (y1 - y2) * (y1 - y2));
	    e.len = len;
	}
    }

    public synchronized void update(Graphics g)
    {
	control();
	switch (viewMode)
	{
	case 0:
	    update1(g);
	    break;
	case 1:
	    update2(g);
	    break;
	case 2:
	    update3(g);
	    break;
	case 3:
	    update4(g);
	    break;
	default:
	    break;
	}
    }

    /**
     * basic drawing
     * 
     * @param g
     */
    void update1(Graphics g)
    {
	Dimension d = getSize();
	if ((offscreen == null) || (d.width != offscreensize.width)
		|| (d.height != offscreensize.height))
	{
	    offscreen = createImage(d.width, d.height);
	    offscreensize = d;
	    if (offgraphics != null)
	    {
		offgraphics.dispose();
	    }
	    offgraphics = offscreen.getGraphics();
	    offgraphics.setFont(getFont());
	}

	offgraphics.setColor(getBackground());
	offgraphics.fillRect(0, 0, d.width, d.height);
	offgraphics.drawImage(backgroundImage, 0, 0, this);
	updateEdgeLen();
	for (int i = 0; i < nedges; i++)
	{
	    Edge e = edges[i];
	    int x1 = (int) nodes[e.from].x;
	    int y1 = (int) nodes[e.from].y;
	    int x2 = (int) nodes[e.to].x;
	    int y2 = (int) nodes[e.to].y;
	    int len = (int)e.len;
	    /*
		(int) Math.sqrt((x1 - x2) * (x1 - x2)
		    + (y1 - y2) * (y1 - y2));
	    e.len = len;
	    */
	    offgraphics.setColor((len < 10) ? arcColor1 : (len < 20 ? arcColor2
		    : arcColor3));
	    offgraphics.drawLine(x1, y1, x2, y2);
	    if (showLength)
	    {
		String lbl = String.valueOf(len);
		offgraphics.setColor(stressColor);
		offgraphics.drawString(lbl, x1 + (x2 - x1) / 2, y1 + (y2 - y1)
			/ 2);
		offgraphics.setColor(edgeColor);
	    }
	}

	FontMetrics fm = offgraphics.getFontMetrics();
	for (int i = 0; i < nnodes; i++)
	{
	    paintNode(offgraphics, nodes[i], fm);
	}

	for (int i = 0; i < ntrucks; i++)
	{
	    trucks[i].draw(offgraphics,this);
	}

	
	g.drawImage(offscreen, 0, 0, null);
    }

    /**
     * update excavators/unload and its list of trucks
     * 
     * @param g
     */
    void update2(Graphics g)
    {
	Dimension d = getSize();
	if ((offscreen == null) || (d.width != offscreensize.width)
		|| (d.height != offscreensize.height))
	{
	    offscreen = createImage(d.width, d.height);
	    offscreensize = d;
	    if (offgraphics != null)
	    {
		offgraphics.dispose();
	    }
	    offgraphics = offscreen.getGraphics();
	    offgraphics.setFont(getFont());
	}

	offgraphics.setColor(getBackground());
	offgraphics.fillRect(0, 0, d.width, d.height);

	offgraphics.setColor(Color.black);
	// draw excavators only
	for (int i = 0, y = 0; i < nnodes; i++)
	{
	    if (nodes[i].device instanceof Excavator)
	    {
		Excavator ex = (Excavator) nodes[i].device;
		Image a = ex.getImage();
		offgraphics.drawString(ex.getId(), 5, 20 + 30 * y);
		offgraphics.drawImage(a, 30, 10 + 30 * y, this);
		offgraphics.drawString(ex.getStrength()+"", 55, 20 + 30 * y);
		//offgraphics.drawString(nodes[i].lbl, 5, 20 + 30 * y);
		int x = 0;
		for( int j=0;j<ntrucks;j++)
		{
		    if( trucks[j].getInstruction().getRoute().getExcavator().getId() == ex.getId() )
		    {
			offgraphics.drawImage(trucks[j].getImage(), 95 + 20*x++, 20 + 30 * y, this);
			offgraphics.drawString(trucks[j].getId(), 95 + 20*x++, 20 + 30 * y);
			//System.out.println( "DD:" + ex.getId() + ":" + trucks[j].getInstruction().getRoute().getExcavator().getId() );
		    }
		}
		y++;
	    }
	}
	// draw unload areas only
	for (int i = 0, y = 0; i < nnodes; i++)
	{
	    if (nodes[i].device instanceof Excavator)
	    {
		Excavator ex = (Excavator) nodes[i].device;
		Image a = ex.getImage();
		offgraphics.drawString(ex.getId(), 5, 20 + 30 * y);
		offgraphics.drawImage(a, 30, 10 + 30 * y, this);
		offgraphics.drawString(ex.getStrength()+"", 55, 20 + 30 * y);
		//offgraphics.drawString(nodes[i].lbl, 5, 20 + 30 * y);
		int x = 0;
		for( int j=0;j<ntrucks;j++)
		{
		    if( trucks[j].getInstruction().getRoute().getExcavator().getId() == ex.getId() )
		    {
			offgraphics.drawImage(trucks[j].getImage(), 95 + 20*x++, 20 + 30 * y, this);
			offgraphics.drawString(trucks[j].getId(), 95 + 20*x++, 20 + 30 * y);
			//System.out.println( "DD:" + ex.getId() + ":" + trucks[j].getInstruction().getRoute().getExcavator().getId() );
		    }
		}
		y++;
	    }
	}
	g.drawImage(offscreen, 0, 0, null);
    }

    static boolean drawOnce = false;
    /**
     * background only drawing
     * 
     * @param g
     */
    void update3(Graphics g)
    {
	Dimension d = getSize();
	if ((offscreen == null) || (d.width != offscreensize.width)
		|| (d.height != offscreensize.height))
	{
	    offscreen = createImage(d.width, d.height);
	    offscreensize = d;
	    if (offgraphics != null)
	    {
		offgraphics.dispose();
	    }
	    offgraphics = offscreen.getGraphics();
	    offgraphics.setFont(getFont());
	}

	offgraphics.setColor(getBackground());
	offgraphics.fillRect(0, 0, d.width, d.height);
	offgraphics.drawImage(backgroundImage, 0, 0, this);
	g.drawImage(offscreen, 0, 0, null);

	if( !drawOnce )
	{
	// display routes
	for( int i=0;i<nroutes;i++)
	{
	    for( Edge e: routes[i].edges)
	    {
		System.out.print( e.from + "-" + e.to + " len:" + e.len + ",");
	    }
	    System.out.println();
	    for( Edge e: routes[i].edges)
	    {
		System.out.print( nodes[e.from].lbl + "-" + nodes[e.to].lbl + " len:" + e.len + ",");
	    }
	    System.out.println();
	}
	drawOnce = true;
	}
    }

    /**
     * draw trucks and its instructions
     * 
     * @param g
     */
    void update4(Graphics g)
    {
	Dimension d = getSize();
	if ((offscreen == null) || (d.width != offscreensize.width)
		|| (d.height != offscreensize.height))
	{
	    offscreen = createImage(d.width, d.height);
	    offscreensize = d;
	    if (offgraphics != null)
	    {
		offgraphics.dispose();
	    }
	    offgraphics = offscreen.getGraphics();
	    offgraphics.setFont(getFont());
	}

	offgraphics.setColor(getBackground());
	offgraphics.fillRect(0, 0, d.width, d.height);

	offgraphics.setColor(Color.black);
	for (int i = 0; i < ntrucks; i++)
	{
	    offgraphics.drawImage(trucks[i].getImage(), 20, 20 + 30 * i, this);
	    offgraphics.drawString( trucks[i].getId(), 10, 20 + 30 * i);
	    offgraphics.drawString( trucks[i].getTruckStatus().toString(), 40, 20 + 30 * i);
	    Edge[] es = trucks[i].getInstruction().getRoute().edges;
	    String routesPath = "";
	    for( int j=0; j<es.length; j++ )
	    {
		routesPath += nodes[es[j].from].lbl + "-" 
			+ nodes[es[j].to].lbl + ":" + es[j].len + " ";
	    }
	    routesPath += trucks[i].getInstruction().getRoute().getDistance();
	    offgraphics.drawString(routesPath, 160, 20 + 30 * i);
	}
	g.drawImage(offscreen, 0, 0, null);
    }

    // 1.1 event handling
    public void mouseClicked(MouseEvent e)
    {
    }

    public void mousePressed(MouseEvent e)
    {
	addMouseMotionListener(this);
	double bestdist = Double.MAX_VALUE;
	int x = e.getX();
	int y = e.getY();
	for (int i = 0; i < nnodes; i++)
	{
	    Node n = nodes[i];
	    double dist = (n.x - x) * (n.x - x) + (n.y - y) * (n.y - y);
	    if (dist < bestdist)
	    {
		pick = n;
		bestdist = dist;
	    }
	}
	pickfixed = pick.fixed;
	pick.fixed = true;
	pick.x = x;
	pick.y = y;
	repaint();
	e.consume();
    }

    public void mouseReleased(MouseEvent e)
    {
	removeMouseMotionListener(this);
	if (pick != null)
	{
	    pick.x = e.getX();
	    pick.y = e.getY();
	    pick.fixed = pickfixed;
	    pick = null;
	}
	repaint();
	e.consume();
    }

    public void mouseEntered(MouseEvent e)
    {
    }

    public void mouseExited(MouseEvent e)
    {
    }

    public void mouseDragged(MouseEvent e)
    {
	pick.x = e.getX();
	pick.y = e.getY();
	repaint();
	e.consume();
    }

    public void mouseMoved(MouseEvent e)
    {
    }

    public void start()
    {
	relaxer = new Thread(this);
	relaxer.start();
    }

    public void stop()
    {
	relaxer = null;
    }

}
